import { expect } from "chai";
import { Database } from "../databases.js";
import { Graph } from "../graphs.js";
import { config } from "./_config.js";

describe("Manipulating graph edges", function () {
  const dbName = `testdb_${Date.now()}`;
  const graphName = `testgraph_${Date.now()}`;
  let system: Database, db: Database;
  let graph: Graph;
  before(async () => {
    system = new Database(config);
    if (Array.isArray(config.url) && config.loadBalancingStrategy !== "NONE")
      await system.acquireHostList();
    await system.createDatabase(dbName);
    db = system.database(dbName);
  });
  after(async () => {
    try {
      await system.dropDatabase(dbName);
    } finally {
      system.close();
    }
  });
  beforeEach(async () => {
    graph = db.graph(graphName);
    await graph.create([
      {
        collection: "knows",
        from: ["person"],
        to: ["person"],
      },
    ]);
    await db.waitForPropagation(
      { pathname: `/_api/gharial/${graph.name}` },
      10000,
    );
  });
  afterEach(async () => {
    await graph.drop();
  });
  describe("graph.get", () => {
    it("should return information about the graph", async () => {
      const info = await graph.get();
      expect(info).to.have.property("name", graphName);
      expect(info).to.have.property("edgeDefinitions");
      expect(info.edgeDefinitions).to.be.instanceOf(Array);
      expect(info.edgeDefinitions.map((e) => e.collection)).to.contain("knows");
      expect(info.edgeDefinitions.length).to.equal(1);
      const edgeDefinition = info.edgeDefinitions.filter(
        (e) => e.collection === "knows",
      );
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.from], [] as string[]),
      ).to.contain("person");
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.to], [] as string[]),
      ).to.contain("person");
    });
  });
  describe("graph.edgeCollections", () => {
    it("should contain edge collection", async () => {
      const info = await graph.edgeCollections();
      expect(info).to.be.instanceOf(Array);
      expect(info.map((c: any) => c.name)).to.contain("knows");
      expect(info.length).to.equal(1);
    });
  });
  describe("graph.listEdgeCollections", () => {
    it("should return all edge collection names", async () => {
      const info = await graph.listEdgeCollections();
      expect(info).to.be.instanceOf(Array);
      expect(info).to.contain("knows");
      expect(info.length).to.equal(1);
    });
  });
  describe("graph.listVertexCollections", () => {
    it("should return all vertex collection names", async () => {
      const info = await graph.listVertexCollections();
      expect(info).to.be.instanceOf(Array);
      expect(info).to.contain("person");
      expect(info.length).to.equal(1);
    });
  });
  describe("graph.addEdgeDefinition", () => {
    it("should add an edgeDefinition to the graph", async () => {
      const info = await graph.addEdgeDefinition({
        collection: "works_in",
        from: ["person"],
        to: ["city"],
      });
      expect(info).to.have.property("name", graphName);
      expect(info).to.have.property("edgeDefinitions");
      expect(info.edgeDefinitions).to.be.instanceOf(Array);
      expect(info.edgeDefinitions.map((e) => e.collection)).to.contain(
        "works_in",
      );
      expect(info.edgeDefinitions.length).to.equal(2);
      const edgeDefinition = info.edgeDefinitions.filter(
        (e) => e.collection === "works_in",
      );
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.from], [] as string[]),
      ).to.contain("person");
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.to], [] as string[]),
      ).to.contain("city");
    });
  });
  describe("graph.replaceEdgeDefinition", () => {
    it("should replace an existing edgeDefinition in the graph", async () => {
      const info = await graph.replaceEdgeDefinition("knows", {
        collection: "knows",
        from: ["person"],
        to: ["city"],
      });
      expect(info).to.have.property("name", graphName);
      expect(info).to.have.property("edgeDefinitions");
      expect(info.edgeDefinitions).to.be.instanceOf(Array);
      expect(info.edgeDefinitions.map((e) => e.collection)).to.contain("knows");
      expect(info.edgeDefinitions.length).to.equal(1);
      const edgeDefinition = info.edgeDefinitions.filter(
        (e) => e.collection === "knows",
      );
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.from], [] as string[]),
      ).to.contain("person");
      expect(
        edgeDefinition.reduce((arr, e) => [...arr, ...e.to], [] as string[]),
      ).to.contain("city");
    });
  });
  describe("graph.removeEdgeDefinition", () => {
    it("should remove an edgeDefinition from the graph", async () => {
      const info = await graph.removeEdgeDefinition("knows");
      expect(info).to.have.property("name", graphName);
      expect(info).to.have.property("edgeDefinitions");
      expect(info.edgeDefinitions).to.be.instanceOf(Array);
      expect(info.edgeDefinitions.length).to.equal(0);
    });
  });
});
